package com.aner.tester.resolver;

import com.aner.tester.generator.data.ObjectGenerator;
import com.aner.tester.generator.param.ValueType;
import com.aner.tester.object.ValueObject;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.stream.Collectors;

/**
 * parameter resolver
 *
 * @author aner
 * @version 1.0
 * @date 2020/12/14 21:42
 */
public class ParameterResolver {

    private static List<Class<? extends Annotation>> valueTypeList = new ArrayList<>();

    static {
        valueTypeList.add(PathVariable.class);
        valueTypeList.add(RequestBody.class);
        valueTypeList.add(RequestParam.class);
    }

    public static List<ValueObject> resolve(Method method) {

        List<ValueObject> valueObjectList = new ArrayList<>();
        Parameter[] parameters = method.getParameters();
        Class[] parameterTypes = method.getParameterTypes();

        for (int i = 0; i < parameters.length; i++) {

            String paramName = parameters[i].getName();
            String paramTypeName = parameterTypes[i].getName();
            String paramSimpleTypeNme = parameterTypes[i].getSimpleName();
            String paramGenericTypeName = parameters[i].getParameterizedType().getTypeName();

            ValueObject valueObject = new ValueObject();
            // the parameter object can not be nested
            valueObject.setLevelCount(ObjectGenerator.getNestedLevelCount());
            valueObject.setName(paramName);
            valueObject.setValueType(getValueType(parameters[i]));
            // basic、 custom and list(without generic) type
            if (paramTypeName.equals(paramGenericTypeName)) {
                valueObject.setGenericCollection(false);
                valueObject.setSimpleTypeName(paramSimpleTypeNme);
                valueObject.setTypeName(paramTypeName);
                valueObject.setGenericTypeNameList(Arrays.asList((paramGenericTypeName)));
                valueObject.setGenericTypeSimpleNameList(Arrays.asList(ResolverFactory.getTypeSimpleName(paramGenericTypeName)));
                // array and list(with generic) type
            } else {
                valueObject.setGenericCollection(true);
                // array type
                if (ResolverFactory.isArray(paramGenericTypeName)) {
                    valueObject.setGenericTypeNameList(Arrays.asList(ResolverFactory.getArrayGenericName(paramGenericTypeName)));
                    valueObject.setGenericTypeSimpleNameList(valueObject.getGenericTypeNameList()
                            .stream()
                            .map(ResolverFactory::getTypeSimpleName)
                            .collect(Collectors.toList()));
                    valueObject.setTypeName(ResolverFactory.getArrayTypeName(paramGenericTypeName));
                    valueObject.setSimpleTypeName(ResolverFactory.getArrayTypeName(paramGenericTypeName));
                    // list(with generic) type
                } else {
                    valueObject.setTypeName(paramTypeName);
                    valueObject.setSimpleTypeName(paramSimpleTypeNme);
                    valueObject.setGenericTypeNameList(Arrays.asList(ResolverFactory.getCollectionGenericName(paramGenericTypeName)));
                    valueObject.setGenericTypeSimpleNameList(valueObject.getGenericTypeNameList()
                            .stream()
                            .map(ResolverFactory::getTypeSimpleName)
                            .collect(Collectors.toList()));
                }
            }
            valueObjectList.add(valueObject);
        }
        return valueObjectList;
    }

    public static String getValueType(Parameter parameter) {
        for(Class cls: valueTypeList) {
            if (parameter.isAnnotationPresent(cls)) {
                return cls.getSimpleName();
            }
        }
        return ValueType.NONE;
    }
}
